
import pandas as pd
import numpy as np
from scipy.stats import entropy
from typing import Literal

class EntropyWeight:
    def __init__(self, data, indicators, objects):
        '''
        data: np.ndarray, shape=(n_objects, n_indicators)
        indicators: list, 指标名
        objects: list, 对象名
        '''
        self.array = data
        self.data = None
        self.n = data.shape[0]
        self.m = data.shape[1]
        self.weight_list = None
        self.scores = None
        self.indicators = indicators
        self.objects = objects

    def data_positivization(self, type: Literal['micro', 'intermediate', 'interval'], i, reference1 = None, reference2 = None):
        '''
        功能：对指标进行正向化处理
        参数：type：指标类型（1：micro极小型，2：intermediate中间型，3：interval区间型）
              i：正在处理的是原始矩阵的哪一列
        返回：正向化处理后的数据矩阵
        '''
        if type == 'micro':  #极小型
            print("第",i,"列是极小型，正向化中...")
            assert reference1 is None and reference2 is None, "极小型不需要额外参数"
            self.array[:, i] = np.max(self.array[:, i])- self.array[:, i]
            return print("第",i,"列极小型处理完成")
        elif type == 'intermediate':  #中间型
            print("第",i,"列是中间型")
            assert reference1 is not None, "中间型指标需要提供参考值"
            m = np.max(abs(self.array[:, i] - reference1))
            self.array[:, i] = 1 - abs(self.array[:, i] - reference1)/m
            return print("第",i,"列中间型处理完成")
        elif type == 'interval':  #区间型
            print("第",i,"列是区间型")
            assert reference1 is not None and reference2 is not None, "区间型指标需要提供两个参考值"
            assert reference1 < reference2, "区间型指标的两个参考值必须满足reference1 < reference2"
            m = np.max((abs(reference1 - np.min(self.array[:, i])), abs(reference2 - np.max(self.array[:, i]))))
            for r in range(self.n):
                if self.array[r, i] < reference1:
                    self.array[r, i] = 1-(reference1-self.array[r, i]) / m
                elif self.array[r, i] > reference2:
                    self.array[r, i] = 1-(self.array[r, i] - reference2) / m
                else:
                    self.array[r, i] = 1
            print("第",i,"列区间型处理完成")
            return print("第", i, "列区间型处理完成")
        
    def normalization(self):
        '''
        功能：对正向化后的矩阵进行标准化处理
        '''
        print("标准化处理中...")
        temp1 = (self.array * self.array).sum(axis=0)
        temp2 = np.tile(temp1, (self.n, 1))  # 将矩阵temp1平铺n行
        self.data = self.array / (temp2 ** 0.5)
        return("标准化处理完成")

    def calc_entropy(self):
        '''计算每个指标的信息熵'''
        entropy_list = []
        for i in range(self.m):
            entropy_list.append(1/np.log(self.n)*entropy(self.data[:,i]))
        return np.array(entropy_list)

    def calc_weight(self):
        '''计算熵权'''
        entropy_list = self.calc_entropy()
        weight_list = 1 - entropy_list
        weight_list = weight_list / np.sum(weight_list)
        self.weight_list = weight_list
        return weight_list

    def calc_score(self):
        '''计算每个对象的加权得分'''
        if self.weight_list is None:
            self.calc_weight()
        scores = np.dot(self.data, self.weight_list)
        self.scores = scores
        return scores

    def best_object(self):
        '''返回得分最高的对象名'''
        if self.scores is None:
            self.calc_score()
        idx = np.argmax(self.scores)
        return self.objects[idx]

    def print_result(self):
        print("各个指标的信息熵权重为：", self.weight_list)
        print("每个对象的综合得分为：", self.scores)
        print("最佳对象是：", self.best_object())


# 示例
if __name__ == "__main__":
    indicators = ['Price', 'Delivery Time', 'Quality']
    suppliers = ['Supplier A', 'Supplier B', 'Supplier C']
    data = np.array([[4, 20, 80], [5, 20, 80], [4, 18, 85]])
    ew = EntropyWeight(data, indicators, suppliers)
    ew.data_positivization('micro', 0)  # 价格为极小型指标
    ew.normalization()
    ew.calc_weight()
    ew.calc_score()
    ew.print_result()